package com.icesoft.base.manager.dao.impl;

import com.icesoft.base.manager.dao.BaseUserInterceptor;
import com.icesoft.base.manager.entity.system.SysOrg;
import com.icesoft.base.manager.helper.AuthUser;
import com.icesoft.base.manager.helper.DataScopeEnum;
import com.icesoft.base.manager.helper.UserPermission;
import com.icesoft.base.manager.service.security.AuthUserService;
import com.icesoft.base.manager.service.system.SysOrgService;
import com.icesoft.core.common.exception.CheckException;
import com.icesoft.core.common.util.ReflectionUtils;
import com.icesoft.core.dao.base.BaseModel;
import com.icesoft.core.dao.criteria.SelectBuilder;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Component
@Slf4j
@AllArgsConstructor
public class DataOrgScopeInterceptor extends BaseUserInterceptor {
    private final SysOrgService sysOrgService;
    private final AuthUserService authUserService;

    private void checkModelScope(BaseModel model, AuthUser user) {
        if (user.getId() == 1 || getOrgIdField(model.getClass()) == null) {
            return;
        }
        int orgId = (int) ReflectionUtils.getFieldValue(model, "orgId");
        Set<Integer> orgIds = user.getPermission().getOrgIds();
        //从缓存中获取权限判断
        if (!orgIds.contains(orgId)) {
            //更新权限再次判断
            orgIds = authUserService.reloadAuthority(user.getUsername()).getPermission().getOrgIds();
            if (!orgIds.contains(orgId) && sysOrgService.load(orgId) != null) {
                log.warn("用户账号[{}]，orgIdList:[{}]没有数据model:[{}],orgId:[{}]的访问权限"
                        , user.getUsername(), user.getPermission().getOrgIds(), model, orgId);
                throw new CheckException("没有数据访问权限");
            }
        }
    }

    @Override
    public void onLoad(BaseModel model, AuthUser user) {
        checkModelScope(model, user);
    }

    @Override
    public void onCreate(BaseModel model, AuthUser user) {
        Field field = getOrgIdField(model.getClass());
        if (field == null) {
            return;
        }
        int orgId = (int) ReflectionUtils.getFieldValue(model, field);
        if (orgId == 0) {
            ReflectionUtils.setFieldValue(model, field, user.getOrgId());
        }
        field = getCompanyIdField(model.getClass());
        if (field != null) {
            int companyId = (int) ReflectionUtils.getFieldValue(model, field);
            if (companyId == 0) {
                ReflectionUtils.setFieldValue(model, field, user.getCompanyId());
            }
        }
        checkModelScope(model, user);
    }

    @Override
    public void onUpdate(BaseModel model, AuthUser user) {
        checkModelScope(model, user);
    }

    @Override
    public void onDelete(BaseModel model, AuthUser user) {
        checkModelScope(model, user);
    }

    @Override
    public void onQuery(SelectBuilder selectBuilder, AuthUser user) {
        if (getOrgIdField(selectBuilder.getFromBuilder().getFromTable()) == null) {
            return;
        }
        SysOrg sysOrg = sysOrgService.load(user.getOrgId());
        if (sysOrg == null) {
            log.warn("用户数据错误，用户账号[{}]没有访问权限", user.getUsername());
            throw new CheckException("用户数据错误，没有设置有效部门");
        }
        DataScopeUserFilter dataScopeUserFilter = new DataScopeUserFilter(selectBuilder.getFromBuilder(), sysOrg);
        UserPermission userPermission = user.getPermission();
        DataScopeEnum.runScopeFilter(userPermission.getSysRoleList(), dataScopeUserFilter);
    }


    private static Map<Class, FieldDTO> orgIdClassMap = new ConcurrentHashMap<>(64);

    private static Field getOrgIdField(Class clazz) {
        FieldDTO fieldDTO = orgIdClassMap.get(clazz);
        if (fieldDTO == null) {
            fieldDTO = new FieldDTO(ReflectionUtils.getDeclaredField(clazz, "orgId"));
            orgIdClassMap.put(clazz, fieldDTO);
        }
        return fieldDTO.getField();
    }

    private static Map<Class, FieldDTO> companyIdClassMap = new ConcurrentHashMap<>(64);

    private static Field getCompanyIdField(Class clazz) {
        FieldDTO fieldDTO = companyIdClassMap.get(clazz);
        if (fieldDTO == null) {
            fieldDTO = new FieldDTO(ReflectionUtils.getDeclaredField(clazz, "companyId"));
            companyIdClassMap.put(clazz, fieldDTO);
        }
        return fieldDTO.getField();
    }

    @AllArgsConstructor
    @Getter
    private static class FieldDTO {
        Field field;
    }
}
